#include "port_bos.h"
#include "b_os.h"
#include "driver/gpio.h"
#include "driver/spi_master.h"
#include "driver/uart.h"

#define UART_PORT_NUM UART_NUM_1 // 使用 UART1
#define BOS_UART_PORT_NUM B_HAL_UART_2
#define UART_BAUD_RATE 115200 // 波特率
#define UART_TX_PIN 10        // TX
#define UART_RX_PIN 9         // RX
#define BUF_SIZE 1024         // 缓冲区大小

#define IO_SPI_ENABLE (0)

static QueueHandle_t sgBosQueue = NULL;
static QueueHandle_t sgBos2MainQueue = NULL;
static uint8_t sgVoiceVolume = 70;
static uint8_t sgVoiceState = 0;
void bLogOutputBytes(uint8_t *pbuf, uint16_t len) { printf("%.*s", len, pbuf); }

void bHalUserInit() {
  printf("user init...\r\n");
  // gpio init...
#ifdef PIN_NUM_DC
  gpio_config_t io_conf = {};
  io_conf.pin_bit_mask = ((1ULL << PIN_NUM_DC) | (1ULL << PIN_NUM_RST) |
                          (1ULL << PIN_NUM_BCKL) | (1ULL << PIN_NUM_CS));
  io_conf.mode = GPIO_MODE_OUTPUT;
  io_conf.pull_up_en = true;
  gpio_config(&io_conf);
  gpio_set_level(PIN_NUM_BCKL, 1);
  gpio_set_level(PIN_NUM_CS, 1);
  gpio_set_level(PIN_NUM_DC, 1);

  io_conf.pin_bit_mask =
      (1ULL << PIN_NUM_KEY_CLEAN) | (1ULL << PIN_NUM_KEY_FEED) |
      (1ULL << PIN_NUM_KEY_LIGHT_UP) | (1ULL << PIN_NUM_KEY_LIGHT_DOWN);
  io_conf.mode = GPIO_MODE_INPUT;
  io_conf.pull_up_en = true;
  gpio_config(&io_conf);
#endif
  // uart init....
#ifdef UART_PORT_NUM
  uart_config_t uart_config = {
      .baud_rate = UART_BAUD_RATE,
      .data_bits = UART_DATA_8_BITS,
      .parity = UART_PARITY_DISABLE,
      .stop_bits = UART_STOP_BITS_1,
      .flow_ctrl = UART_HW_FLOWCTRL_DISABLE,
  };

  ESP_ERROR_CHECK(uart_param_config(UART_PORT_NUM, &uart_config));
  ESP_ERROR_CHECK(uart_set_pin(UART_PORT_NUM, UART_TX_PIN, UART_RX_PIN,
                               UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
  ESP_ERROR_CHECK(
      uart_driver_install(UART_PORT_NUM, BUF_SIZE * 2, 0, 0, NULL, 0));
#endif
  // spi init ...
#ifdef PIN_NUM_MISO
#if IO_SPI_ENABLE
  io_conf.pin_bit_mask = (1ULL << PIN_NUM_MOSI) | (1ULL << PIN_NUM_CLK);
  io_conf.mode = GPIO_MODE_OUTPUT;
  io_conf.pull_up_en = true;
  gpio_config(&io_conf);
  gpio_set_level(PIN_NUM_CLK, 1);
#else
  esp_err_t ret;
  spi_bus_config_t buscfg = {.miso_io_num = PIN_NUM_MISO,
                             .mosi_io_num = PIN_NUM_MOSI,
                             .sclk_io_num = PIN_NUM_CLK,
                             .quadwp_io_num = -1,
                             .quadhd_io_num = -1,
                             .max_transfer_sz = 0};
  spi_device_interface_config_t devcfg = {
      .clock_speed_hz = 40 * 1000 * 1000, // Clock out at 10 MHz
      .mode = 3,                          // SPI mode 0
      .spics_io_num = -1,                 // CS pin
      .queue_size = 7, // We want to be able to queue 7 transactions at a time
  };
  ret = spi_bus_initialize(SPI2_HOST, &buscfg, SPI_DMA_CH_AUTO);
  ESP_ERROR_CHECK(ret);
  ret = spi_bus_add_device(SPI2_HOST, &devcfg, &sg_spi_handle);
  ESP_ERROR_CHECK(ret);
  uint8_t dat = 0;
  spi_transaction_t trans = {
      .length = 8,
      .tx_buffer = &dat,
      .rx_buffer = &dat,
  };
  spi_device_transmit(sg_spi_handle, &trans);
#endif
#endif
  printf("user init.. end..\r\n");
}

#include "freertos/FreeRTOS.h"
#include "freertos/portmacro.h"
#include "freertos/task.h"

uint32_t bHalGetSysTick() { return xTaskGetTickCount(); }

uint64_t bHalGetSysTickPlus() {
  TimeOut_t tm;
  vTaskInternalSetTimeOutState(&tm);
  uint64_t tick = tm.xOverflowCount;
  tick = tm.xTimeOnEntering + (tick << (8 * sizeof(uint32_t)));
  return tick;
}

int _bVoiceProtCb(bProtoCmd_t cmd, void *param);
int _bVoiceProtUartCb(uint8_t *pbuf, uint16_t len, void *user_data);

static bProtSrvId_t bpVoiceProtId = NULL;
static bTaskAttr_t bUartRecvTaskAttr;
static bTaskAttr_t bUartSendTaskAttr;
bHAL_UART_CREATE_ATTR(bVoiceProtUartAttr, BUF_SIZE, 50, _bVoiceProtUartCb,
                      NULL);
B_PROT_SRV_CREATE_ATTR(bVoiceProtAttr, "bos", _bVoiceProtCb);

int _bVoiceProtGetInfo(bProtoInfoType_t type, uint8_t *buf, uint16_t buf_len) {
  if (type == B_PROTO_INFO_DEVICE_ID) {
    memset(buf, 0xff, buf_len);
  } else if (type == B_PROTO_INFO_GET_VOICE_VOLUME) {
    buf[0] = sgVoiceVolume;
  } else if (type == B_PROTO_INFO_GET_VOICE_STAT) {
    buf[0] = sgVoiceState;
  }
  return 0;
}

typedef struct {
  uint8_t *pbuf;
  uint16_t len;
  struct list_head node;
} bUartTxData_t;

LIST_HEAD(sg_uart_tx_list);

void bPorotcolSendData(uint8_t *pbuf, uint16_t len) {
  bUartTxData_t *p = (bUartTxData_t *)malloc(sizeof(bUartTxData_t));
  if (p == NULL) {
    return;
  }
  p->pbuf = malloc(len);
  if (p->pbuf == NULL) {
    free(p);
    return;
  }
  memcpy(p->pbuf, pbuf, len);
  p->len = len;
  list_add_tail(&p->node, &sg_uart_tx_list);
}

static void _bBabyOS2AppMsg(int type, uint8_t *pmsg, uint16_t msg_len,
                            void (*release)(void *)) {
  if (sgBos2MainQueue == NULL) {
    return;
  }
  bMsgInfo_t msg;
  msg.type = type;
  msg.msg = pmsg;
  msg.msg_len = msg_len;
  msg.release = release;
  xQueueSend(sgBos2MainQueue, &msg, 0);
}

int _bVoiceProtCb(bProtoCmd_t cmd, void *param) {
  b_log("cmd:%d\r\n", cmd);
  if (cmd == B_PROTO_DEVICEINFO) {
    bProtoDevInfo_t *pinfo = (bProtoDevInfo_t *)param;
    if (param == NULL) {
      return -1;
    }
    uint8_t *ptmp = calloc(1, strlen((const char *)pinfo->version) +
                                  strlen((const char *)pinfo->name) + 1 + 1);
    if (ptmp == NULL) {
      return -1;
    }
    memcpy(ptmp, pinfo->version, strlen((const char *)pinfo->version));
    ptmp[strlen((const char *)ptmp)] = ',';
    memcpy(&ptmp[strlen((const char *)ptmp)], pinfo->name,
           strlen((const char *)pinfo->name));
    _bBabyOS2AppMsg(MSG_TYPE_DEVICE_INFO, ptmp,
                    strlen((const char *)pinfo->version) +
                        strlen((const char *)pinfo->name) + 1 + 1,
                    free);
  } else if (cmd == B_PROTO_SETCFGNET_MODE) {
    bProtoCfgNetMode_t *pcfginfo = (bProtoCfgNetMode_t *)param;
    if (pcfginfo == NULL) {
      return -1;
    }
    uint8_t *ptmp = calloc(1, strlen((const char *)pcfginfo->ssid) + 1);
    if (ptmp == NULL) {
      return -1;
    }
    memcpy(ptmp, pcfginfo->ssid, strlen((const char *)pcfginfo->ssid));
    _bBabyOS2AppMsg(MSG_TYPE_CFGNET_MODE, ptmp,
                    strlen((const char *)pcfginfo->ssid) + 1, free);
  } else if (cmd == B_PROTO_SET_VOICE_VOLUME) {
    uint8_t *ptmp = (uint8_t *)param;
    _bBabyOS2AppMsg(MSG_TYPE_VOICE_VOLUME, (void *)(ptmp[0]), 1, NULL);
  } else if (cmd == B_PROTO_REQ_FILE_DATA) {
    bProtoReqFileData_t *preqdata = (bProtoReqFileData_t *)param;
    bMsgFwReqData_t *preqdata_app =
        (bMsgFwReqData_t *)malloc(sizeof(bMsgFwReqData_t));
    if (preqdata_app != NULL) {
      preqdata_app->offset = preqdata->offset;
      preqdata_app->size = preqdata->size;
      _bBabyOS2AppMsg(MSG_TYPE_REQ_OTA_DATA, (uint8_t *)preqdata_app,
                      sizeof(bMsgFwReqData_t), free);
    }
  }
  return 0;
}
int _bVoiceProtUartCb(uint8_t *pbuf, uint16_t len, void *user_data) {
  uint8_t tmp[64];
  int o_len = sizeof(tmp);
  o_len = bProtSrvParse(bpVoiceProtId, pbuf, len, tmp, o_len);
  if (o_len > 0) {
    bPorotcolSendData(tmp, o_len);
  }
  return 0;
}

PT_THREAD(_bUartRecvTask)(struct pt *pt, void *arg) {
  bHalItParam_t param;
  uint8_t tmp[128];
  int len = 0;
  B_TASK_INIT_BEGIN();
  b_log("uart recv.....\r\n");
  bpVoiceProtId = bProtSrvInit(&bVoiceProtAttr, _bVoiceProtGetInfo);
  bHalUartReceive(BOS_UART_PORT_NUM, &bVoiceProtUartAttr);
  B_TASK_INIT_END();
  PT_BEGIN(pt);
  while (1) {
    len = uart_read_bytes(UART_PORT_NUM, tmp, sizeof(tmp), 0);
    if (len > 0) {
      b_log_hex(tmp, len);
      param._uart.pbuf = tmp;
      param._uart.len = len;
      bHalItInvoke(B_HAL_IT_UART_RX, B_HAL_UART_2, &param);
    }
    bTaskYield(pt);
  }
  PT_END(pt);
}

PT_THREAD(_bUartSendTask)(struct pt *pt, void *arg) {
  B_TASK_INIT_BEGIN();
  B_TASK_INIT_END();
  PT_BEGIN(pt);
  while (1) {
    bTaskDelayMs(pt, 100);
    if (list_empty(&sg_uart_tx_list) == 0) {
      struct list_head *pos = NULL;
      bUartTxData_t *ptx_data = NULL;
      pos = sg_uart_tx_list.next;
      __list_del(pos->prev, pos->next);
      ptx_data = list_entry(pos, bUartTxData_t, node);
      if (ptx_data->pbuf) {
        b_log_hex(ptx_data->pbuf, ptx_data->len);
        bHalUartSend(BOS_UART_PORT_NUM, ptx_data->pbuf, ptx_data->len);
        free(ptx_data->pbuf);
        ptx_data->pbuf = NULL;
      }
      free(ptx_data);
      ptx_data = NULL;
    }
  }
  PT_END(pt);
}

void bBabyOSHandler(void *arg) {
  bMsgInfo_t msg;
  uint8_t proto_buf[64];
  int len = 0;
  bInit();
  bTaskCreate("uart recv", _bUartRecvTask, NULL, &bUartRecvTaskAttr);
  bTaskCreate("uart send", _bUartSendTask, NULL, &bUartSendTaskAttr);
  b_log("enter................\r\n");
  for (;;) {
    memset(&msg, 0, sizeof(msg));
    if (xQueueReceive(sgBosQueue, &msg, 1)) {
      b_log("msg:%d %p %d\r\n", msg.type, msg.msg, msg.msg_len);
      switch (msg.type) {
      case MSG_TYPE_VOICE_STATE: {
        b_log("voice_stat:::::::::::%d\r\n", msg.msg[0]);
        sgVoiceState = msg.msg[0];
        proto_buf[0] = sgVoiceState;
        len = bProtSrvPackage(bpVoiceProtId, B_PROTO_GET_VOICE_STATE, proto_buf,
                              sizeof(proto_buf));
        if (len > 0) {
          bPorotcolSendData(proto_buf, len);
        }
      } break;
      case MSG_TYPE_TSL_INVOKE: {
        uint8_t *ptmp_tsl_buf = (uint8_t *)malloc(msg.msg_len + 16);
        if (ptmp_tsl_buf) {
          memset(ptmp_tsl_buf, 0, msg.msg_len + 16);
          memcpy(ptmp_tsl_buf, msg.msg, msg.msg_len);
          len = bProtSrvPackage(bpVoiceProtId, B_PROTO_TSL_INVOKE, ptmp_tsl_buf,
                                msg.msg_len + 16);
          if (len > 0) {
            bPorotcolSendData(ptmp_tsl_buf, len);
          }
          free(ptmp_tsl_buf);
          ptmp_tsl_buf = NULL;
        } else {
          b_log_e("malloc fail..\r\n");
        }
      } break;
      case MSG_TYPE_VOICE_VOLUME: {
        int volume = (int)msg.msg;
        sgVoiceVolume = (uint8_t)(volume & 0xff);
        proto_buf[0] = sgVoiceVolume;
        len = bProtSrvPackage(bpVoiceProtId, B_PROTO_GET_VOICE_VOLUME,
                              proto_buf, sizeof(proto_buf));
        if (len > 0) {
          bPorotcolSendData(proto_buf, len);
        }
      } break;
      case MSG_TYPE_DEVICE_INFO: {
        len = bProtSrvPackage(bpVoiceProtId, B_PROTO_DEVICEINFO, proto_buf,
                              sizeof(proto_buf));
        if (len > 0) {
          bPorotcolSendData(proto_buf, len);
        }
      } break;
      case MSG_TYPE_NOTIFY_OTA: {
        uint8_t *pfileinfo = malloc(sizeof(bProtoFileInfo_t) + 16);
        if (pfileinfo == NULL) {
          b_log_e("malloc fail..\r\n");
          break;
        }
        bProtoFileInfo_t *pinfo = (bProtoFileInfo_t *)pfileinfo;
        bMsgFwinfo_t *pfwinfo = (bMsgFwinfo_t *)msg.msg;
        pinfo->fcrc32 = pfwinfo->crc32;
        memcpy(pinfo->name, pfwinfo->filename, strlen(pfwinfo->filename));
        pinfo->size = pfwinfo->size;
        len = bProtSrvPackage(bpVoiceProtId, B_PROTO_OTA_FILE_INFO, pfileinfo,
                              sizeof(bProtoFileInfo_t) + 16);
        if (len > 0) {
          b_log_hex(pfileinfo, len);
          bPorotcolSendData(pfileinfo, len);
        }
        free(pfileinfo);
        pfileinfo = NULL;
      } break;
      case MSG_TYPE_NOTIFY_OTA_DATA: {
        bMsgFwData_t *pfwdata = (bMsgFwData_t *)msg.msg;
        if (pfwdata && pfwdata->size > 0 && pfwdata->pdata != NULL) {
          uint8_t *pfiledata =
              (uint8_t *)malloc(sizeof(bProtoFileData_t) + pfwdata->size + 16);
          if (pfiledata) {
            bProtoFileData_t *pfile = (bProtoFileData_t *)pfiledata;
            pfile->offset = pfwdata->offset;
            pfile->size = pfwdata->size;
            pfile->dat = pfwdata->pdata;
            len =
                bProtSrvPackage(bpVoiceProtId, B_PROTO_FILE_DATA, pfiledata,
                                sizeof(bProtoFileData_t) + pfwdata->size + 16);
            if (len > 0) {
              b_log_hex(pfiledata, len);
              bPorotcolSendData(pfiledata, len);
            }
            free(pfiledata);
            pfiledata = NULL;
          }
          free(pfwdata->pdata);
          pfwdata->pdata = NULL;
        }
      } break;
      default:
        break;
      }
      if (msg.release) {
        if (msg.msg) {
          msg.release(msg.msg);
          msg.msg = NULL;
        }
      }
    }
    bExec();
  }
}

void bBabyOSRun(void *queue) {
  b_assert_log(queue != NULL);
  sgBosQueue = xQueueCreate(10, sizeof(bMsgInfo_t));
  b_assert_log(sgBosQueue != NULL);
  sgBos2MainQueue = queue;
  xTaskCreate(bBabyOSHandler, "bos", 4096, NULL, uxTaskPriorityGet(NULL), NULL);
}

void bBabyOSSendMsg(int type, uint8_t *pmsg, uint16_t msg_len,
                    void (*release)(void *)) {
  if (sgBosQueue == NULL) {
    return;
  }
  bMsgInfo_t msg;
  msg.type = type;
  msg.msg = pmsg;
  msg.msg_len = msg_len;
  msg.release = release;
  xQueueSend(sgBosQueue, &msg, 0);
}
